/*
 * MapGen - the horrible terrible map generator
 *
 * Copyright 2007 James W. Hofmann
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#if flash9
	import flash.Lib;
#end

import lib3f.LCG;
import lib3f.IntPoint;
import lib3f.Dist;

class MapGen
{

	static public var entrypoints : Array<GameTile>;
	static public var castles : Array<GameTile>;

	// use textures to accent outlines
	// areas around castles
	// coasts

	// entry points will get their own graphics later
	
	// first place stuff
	// three castles, three entry points
	// all should be four squares apart from each other
	
	static function pickpoints() : Array<IntPoint>
	{
		var points : Array<IntPoint> = new Array();
		for (n in 0...6)
		{
			var np : IntPoint = new IntPoint(0,0);
			while (((np.x+np.y) > 36) ||
				((np.x+np.y) < 14) ||
				((np.x-np.y) > 20) ||
				((np.x-np.y) < -20)
				|| (np.x<1) || (np.y<1) ||
				   (np.x>24) || (np.y>24)
				) 
			{
				np.x = LCG.randInt(23)+1;
				np.y = LCG.randInt(23)+1;				
			}
			points.push(np);
		}
		return points;
	}
	
	public static function randpoint() : IntPoint
	{
			var np : IntPoint = new IntPoint(0,0);
			while (outofbounds(np.x,np.y))
			{
				np.x = LCG.randInt(6)+14;
				np.y = LCG.randInt(6)+14;		
			}			
		return np;
	}

	public static function brush(p : IntPoint, width : Int, water : Bool)
	{
		var style : Int;
		if (water)
		{
			style = -1;
		}
		else
		{
			style = LCG.randInt(4);
		}
		
		var px : Int = Std.int(p.x);
		var py : Int = Std.int(p.y);

		
		if (!outofbounds(px,py)) Game.cur.map[px][py].terrain = style;
		
		if (width>1)
		{
			if (!outofbounds(px-1,py)) Game.cur.map[px-1][py].terrain = style;
			if (!outofbounds(px+1,py)) Game.cur.map[px+1][py].terrain = style;
			if (!outofbounds(px,py-1)) Game.cur.map[px][py-1].terrain = style;
			if (!outofbounds(px,py+1)) Game.cur.map[px][py+1].terrain = style;
		}
		if (width>2)
		{
			if (!outofbounds(px-2,py)) Game.cur.map[px-2][py].terrain = style;
			if (!outofbounds(px+2,py)) Game.cur.map[px+2][py].terrain = style;
			if (!outofbounds(px,py-2)) Game.cur.map[px][py-2].terrain = style;
			if (!outofbounds(px,py+2)) Game.cur.map[px][py+2].terrain = style;
			if (!outofbounds(px-1,py+1)) Game.cur.map[px-1][py+1].terrain = style;
			if (!outofbounds(px+1,py-1)) Game.cur.map[px+1][py-1].terrain = style;
			if (!outofbounds(px-1,py-1)) Game.cur.map[px-1][py-1].terrain = style;
			if (!outofbounds(px+1,py+1)) Game.cur.map[px+1][py+1].terrain = style;
		
		}
	
	}
	
	public static function outofbounds(x : Int, y : Int) : Bool
	{
		return  (((x+y) > 37) ||
				 ((x+y) < 13) || (x<0) || (y<0) || (x>25) || (y>25));
	}
	
	public static function walk(np : IntPoint) : IntPoint
	{
		var op = new IntPoint(np.x, np.y);
		op.x + LCG.randInt(2) - 1;
		op.y + LCG.randInt(2) - 1;				
		while (outofbounds(Std.int(np.x),Std.int(np.y))) 
			{
				op.x + LCG.randInt(2) - 1;
				op.y + LCG.randInt(2) - 1;
				if(op.x<0) op.x = 0;
				else if(op.x>25) op.x=25;
				if(op.y<0) op.y = 0;
				else if(op.y>25) op.y=25;
			}
		return np;
	}
	
	public static function genmap()
	{

		for (n in Graph.iter(Game.cur.map))
		{
			n.terrain=-1;
		}
	
		// generate some land
		// do it "snakystrips" style like in openexplore

		var heightmap : Array<Array<Int>> = new Array();
		
		var noisebias = LCG.randInt(50)+25;
		
		for (x in 0...26)
		{
			var next : Array<Int> = new Array();
			for (y in 0...26)
			{
				next.push(-4*Std.int(Math.abs(13-x+13-y))+LCG.randInt(150)+noisebias-Std.int(Dist.Integer(x,13,y,13)*20));
			}
			heightmap.push(next);
		}

		var variancebias = LCG.randInt(100);
		
		for (n in 0...100)
		{
			var curp = randpoint();
			
			var length = LCG.randInt(20)+2;
			
			var variance = LCG.randInt(200)-variancebias;
			
			for (n in 0...length)
			{
				heightmap[curp.x][curp.y]=heightmap[curp.x][curp.y]+variance;
				curp = walk(curp);
			}
			
		}

		for (n in Graph.iter(Game.cur.map))
		{
			if (heightmap[n.x][n.y]>0)
				n.terrain=LCG.randInt(4);
			else
				n.terrain=-1;
		}
		
		// leave a one tile border around the edges
		
		
		for (x in 0...25)
		{
			brush(new IntPoint(x,0),1,true);
			brush(new IntPoint(x,25),1,true);
		}

		for (y in 0...25)
		{
			brush(new IntPoint(0,y),1,true);
			brush(new IntPoint(25,y),1,true);
		}

		// add entry points and castles
		
		var entries : Array<IntPoint>;
		var centries : Array<IntPoint>;
		
		var cont = true;
		
		while (cont)
		{
			centries = new Array();
			for (entry in 0...3)
			{
				var np = new IntPoint(LCG.randInt(24)+1,LCG.randInt(24)+1);
				var cent = new IntPoint(13,13);
				while (
					Dist.IntPoint(np, cent)>8
					) 
				{
					np.x = LCG.randInt(24)+1;
					np.y = LCG.randInt(24)+1;				
				}
				centries.push(np);
			}
			if (
				Dist.IntPoint(centries[0], centries[1])>3 &&
				Dist.IntPoint(centries[0], centries[2])>3 &&
				Dist.IntPoint(centries[1], centries[2])>3
				)
				cont = false;		
		}
		
		cont = true;
		
		while (cont)
		{
			entries = new Array();
			for (entry in 0...3)
			{
				var np = new IntPoint(LCG.randInt(24)+1,LCG.randInt(24)+1);
				while (
					((np.x+np.y) != 11 && (np.x+np.y) != 52-11)
					) 
				{
					np.x = LCG.randInt(24)+1;
					np.y = LCG.randInt(24)+1;				
				}
				entries.push(np);
			}
			if (
				entries[0].x!=entries[1].x &&
				entries[0].x!=entries[2].x &&
				entries[1].x!=entries[2].x &&
				entries[0].y!=entries[1].y &&
				entries[0].y!=entries[2].y &&
				entries[1].y!=entries[2].y
				)
				cont = false;
		}
			
		for (np in entries)
		{
			brush(np,3,true);
		}
		
		for (np in centries)
		{

			// force at least the immediate area around the castle to be there

			Game.cur.map[np.x][np.y].terrain = 5;
			Game.cur.map[np.x+1][np.y].terrain = 5;
			Game.cur.map[np.x+1][np.y+1].terrain = 5;
			Game.cur.map[np.x+1][np.y-1].terrain = 5;
			Game.cur.map[np.x-1][np.y].terrain = 5;
			Game.cur.map[np.x-1][np.y+1].terrain = 5;
			Game.cur.map[np.x-1][np.y-1].terrain = 5;
			Game.cur.map[np.x][np.y+1].terrain = 5;
			Game.cur.map[np.x][np.y-1].terrain = 5;

			brush(new IntPoint(np.x-1, np.y+1),2,false);
			brush(new IntPoint(np.x+1, np.y-1),2,false);
			brush(new IntPoint(np.x-1, np.y-1),2,false);
			brush(new IntPoint(np.x+1, np.y+1),2,false);
		}
		
		for (np in entries)
		{
			Creep.addentry(np);
			entrypoints.push(Game.cur.map[np.x][np.y]);
		}
		
		for (np in centries)
		{
			FortressManager.addcastle(Std.int(np.x),Std.int(np.y));
			castles.push(Game.cur.map[np.x][np.y]);
		}

		for (np in centries)
		{
			FortressManager.fencecastle(Std.int(np.x),Std.int(np.y));		
		}
				
		
	}

	static public function buildfromarrays(maparray : Array<Array<Int>>, fortarray : Array<Array<Int>>)
	{
	
		for (n in Graph.iter(Game.cur.map))
		{
			if (maparray[n.x][n.y]<0)
				n.terrain = -1;
			else
				n.terrain = maparray[n.x][n.y];
			if (fortarray[n.x][n.y]>0)
				FortressManager.add(n.x, n.y, fortarray[n.x][n.y]);		
		}
	
	}
	
}
